﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////

// Reviewed

#pragma once
#include "GcmpMathInterface.h"
#include "Vector3d.h"
#include "Matrix4d.h"
#include "Matrix4dUtils.h"
#include "GcmpApiDeclaration.h"

#include <string>
#include <vector>

namespace gcmp
{
    /// \brief 三维包围盒
    ///
    /// 
    class GCMP_MATH_INTERFACE_EXPORT Box3d
    {
    public:
        DEFINE_VISUAL_TYPE_DOUBLE()

    public:

        /// \brief  创建一个空包围盒
        ///
        /// \return 空包围盒
        static Box3d CreateEmptyBox3d();

        /// \brief   通过最小点， 最大点创建Box3d
        ///
        /// \param min  最小点
        /// \param max  最大点
        /// \return 
        static Box3d Create(const Vector3d& minPoint, const Vector3d& maxPoint);

        /// \brief   判断包围盒是否为空
        ///
        /// \return  true：成功，false：失败
        bool IsEmpty() const;

        /// \brief      缩放包围盒，以(0,0,0)为中心进行缩放
        ///
        /// \param scaleX  X方向缩放比例
        /// \param scaleY  Y方向缩放比例
        /// \param scaleZ  Z方向缩放比例
        /// \return   true：成功，false：失败
        bool ScaleBasedOnOriginPoint(double scaleX,double scaleY, double scaleZ);


        /// \brief   缩放包围盒，以包围盒中心点为中心按比例缩放
        ///
        /// \param scaleX  X方向缩放比例
        /// \param scaleY  Y方向缩放比例
        /// \param scaleZ  Z方向缩放比例
        /// \return  true：成功，false：失败
        GCMP_OBSOLETE_API(2023 / 10 / 08, Box3d::ScaleBasedOnBoxCenterPoint(double, double, double))
        bool ScaleBasedOnCenterPoint(double scaleX, double scaleY, double scaleZ);

        /// \brief   缩放包围盒，以包围盒中心点为中心按比例缩放
        ///          scale在[0,1)表示缩小，1表示不变，大于1表示放大
        /// \param scaleX  X方向缩放比例
        /// \param scaleY  Y方向缩放比例
        /// \param scaleZ  Z方向缩放比例
        /// \return  true：成功，false：失败
        bool ScaleBasedOnBoxCenterPoint(double scaleX, double scaleY, double scaleZ);

        /// \brief   缩放包围盒，以包围盒中心点为中心,将包围盒向各个面方向缩放固定尺寸
        ///
        /// \param size  缩放尺寸，正数放大，负数缩小，负数绝对值不能大于box最短边的1/2，否则缩放失败
        /// \return  true：成功，false：失败
        bool ExpandFixedSize(double size);

        /// \brief   当前包围盒和传入点合并，并设置当前包围盒为合并的结果
        ///
        /// \param point  传入点
        /// \return true：成功，false：失败
        bool MergePoint(const Vector3d & point);

        /// \brief   当前包围盒和传入包围盒合并，并设置当前包围盒为合并后的结果
        ///
        /// \param box  传入包围盒
        /// \return  true：成功，false：失败
        bool MergeBox(const Box3d & box);

        /// \brief    平移当前包围盒
        ///
        /// \param translation  平移量
        /// \return   true：成功，false：失败
        bool Translate(const Vector3d & translation);

        /// \brief  矩阵转换当前包围盒
        ///
        /// \param mat  矩阵
        /// \return   true：成功，false：失败
        bool Transform(const Matrix4d & mat);

        /// \brief    获得当前包围盒和传入包围盒的交集
        ///
        /// \param box  传入包围盒
        /// \return  交集
        Box3d GetIntersectBox(const Box3d& box) const;

        /// \brief    判断是否和传入的包围盒相交
        ///
        /// \param box  传入包围盒
        /// \return   true：相交，false：不相交
        bool IsIntersect(const Box3d & box, double tolerance = Constants::DOUBLE_EPS) const;

        /// \brief   获得包围盒的最小点
        ///
        /// \return  最小点
        const Vector3d& GetMinPoint() const;

        /// \brief    获得包围盒的最大点
        ///
        /// \return  最大点
        const Vector3d& GetMaxPoint() const;

        /// \brief    获得包围盒的中心点
        /// \return 中心点
        Vector3d GetCenter() const;

        /// \brief 在给定误差内，判断当前包围盒是否包含传入点（在包围盒内或在包围盒上）
        ///
        /// \param point  传入点
        /// \param tolerance  给定误差
        /// \return true: 包含， false: 不包含
        bool Contain(const Vector3d & point, double tolerance = Constants::DOUBLE_EPS) const;

    public:

        /// \brief  构造空的包围盒
        ///  
        explicit Box3d();

        /// \brief    根据包围盒的最小点和最大点构造包围盒
        ///
        /// \param minPoint  最小点
        /// \param maxPoint  最大点
        /// 
        explicit Box3d(const Vector3d & minPoint, const Vector3d & maxPoint);

        /// \brief   拷贝构造包围盒
        ///
        /// \param box  传入包围盒
        ///
        Box3d(const Box3d & box);

        /// \brief   赋值构造包围盒
        ///
        /// \param box  传入包围盒
        /// \return  当前包围盒
        Box3d& operator = (const Box3d & box);

        /// \brief    设置包围盒的最小点和最大点
        ///
        /// \param minPoint  最小点
        /// \param maxPoint   最大点
        /// \return  true: 成功; false: 失败
        bool Set(const Vector3d & minPoint, const Vector3d & maxPoint);

        /// \brief     获得包围盒的8个角点
        ///
        /// \param corner   返回角点数组
        /// \return 
        void GetCorners(Vector3d corner[8]) const;


        /// \brief   获得包围盒的长度，宽度和高度
        ///
        /// \return  X坐标为长度，Y坐标为宽度，Z坐标为高度
        Vector3d GetSize() const;

        /// \brief  初始化包围盒
        ///
        /// \return 
        void MakeEmpty();

        /// \brief    判断点是否在包围盒内部（不在包围盒上）
        ///
        /// \param point  给定点
        /// \param tolerance  给定误差
        /// \return  true: 在包围盒的内部 ，false: 不在包围盒的内部
        bool IsPointInBox(const Vector3d & point, double tolerance = Constants::DOUBLE_EPS) const;

        /// \brief   判断点是否在包围盒表面上
        ///
        /// \param point  给定点
        /// \param tolerance  给定误差
        /// \return    true: 在包围盒的表面上 ，false: 不在包围盒的表面上
        bool IsPointOnBox(const Vector3d & point, double tolerance = Constants::DOUBLE_EPS) const;

        /// \brief   判断线段是否和包围盒相交
        ///
        /// \param rStartPt  线段起点
        /// \param rEndPt  线段终点
        /// \param tolerance  给定误差
        /// \return   true: 相交 ，false: 不相交
        bool IsIntersect(const Vector3d & rStartPt, const Vector3d & rEndPt, double tolerance = Constants::DOUBLE_EPS) const;

        /// \brief  在给定误差下，包围盒是否在传入包围内，包括内相切
        ///
        /// \param box   传入包围盒
        /// \param tolerance  给定误差
        /// \return   true:  当前包围盒在传入包围盒内，false:  当前包围盒不在传入包围盒内
        bool IsIn(const Box3d & box, double tolerance = Constants::DOUBLE_EPS) const;

        /// \brief 把包围盒对象按照规定格式字符串输出
        ///
        /// \return 字符串
        std::wstring GetStringForDebug() const;

        /// \brief  根据字符串生成包围盒
        ///
        /// \param str  传入的字符串
        /// \return  生成的包围盒
        static Box3d LoadFromString(const std::wstring& str);

    private:
        Vector3d m_min;
        Vector3d m_max;
    };

    /// \brief     Minkowski Difference
    ///
    /// \param pt b1   传入的包围盒1
    /// \param pt b2   传入的包围盒2
    /// \return 运算结果包围盒
    GCMP_MATH_INTERFACE_EXPORT Box3d operator -(const Box3d & b1, const Box3d & b2);

    /// \brief    Minkowski Sum
    ///
    /// \param pt b1   传入的包围盒1
    /// \param pt b2   传入的包围盒2
    /// \return 运算结果包围盒
    GCMP_MATH_INTERFACE_EXPORT Box3d operator +(const Box3d & b1, const Box3d & b2);

    /// \brief     逻辑等号操作符，判断两个包围盒的是否相等
    ///
    /// \param pt b1   传入的包围盒1
    /// \param pt b2   传入的包围盒2
    /// \return true: 相等，false:  不相等
    GCMP_MATH_INTERFACE_EXPORT bool operator ==(const Box3d& b1, const Box3d& b2);

    /// \brief     逻辑不等号操作符，判断两个包围盒是否不相等
    ///
    /// \param pt b1   传入的包围盒1
    /// \param pt b2   传入的包围盒2
    /// \return true: 相等，false:  不相等
    GCMP_MATH_INTERFACE_EXPORT bool operator !=(const Box3d& b1, const Box3d& b2);
}

